/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

using System;

using ByteArrayCompare = argot.util.ByteArrayCompare;
using ReferenceTypeMap = argot.ReferenceTypeMap;
using TypeException = argot.TypeException;
using TypeHelper = argot.TypeHelper;
using TypeLibrary = argot.TypeLibrary;

namespace argot.network
{
	
	
	public class DynamicClientTypeMap
	:ReferenceTypeMap
	{
		private TypeClient _typeClient;
		
		// This resolveStack is used to look for ensuring that we don't
		// end up in an endless loop trying to resolve self referenced
		// or looping type definitions.
		
		private System.Collections.ArrayList _resolveStack;
		
		public DynamicClientTypeMap(TypeLibrary library, TypeClient client):base(library, null)
		{
			ReferenceMap = this;
			
			_typeClient = client;
			_resolveStack = new System.Collections.ArrayList();
		}
		
		private int resolveType(int systemId)
		{
			
			// See if the type has already been mapped.
			int x;
			
			try
			{
				x = base.GetId(systemId);
			}
			catch (TypeException)
			{
				x = NOTYPE;
			}
			
			if (x != NOTYPE)
				return x;
			
			// No mapping..  Map this type.
			// Requires the Name and structure.
			// The server has the definative map.  So we send
			// the details over and expect a response of the id
			// to use.
			System.String name = Library.GetName(systemId);
			
			if (_resolveStack.Contains(name))
			{
				// If we have a self referenced type we first need to get
				// and ID for the Dynamic type map.  From that we can then
				// check the full definition because we can describe it.
				
				try
				{
					x = _typeClient.ReserveType(name);
				}
				catch (System.IO.IOException e)
				{				
					throw new TypeException(e.Message);
				}
			}
			else
			{
				// We haven't resolved the type yet so put it on the stack
				// and resolve it.
				_resolveStack.Add(name);
				
				// As we write out the definition of this type we may
				// come accross other types that must be resolved.  This
				// will recurively resolve each type.
				
				byte[] definition = TypeHelper.ToByteArray(this, Library.GetStructure(systemId));
				
				try
				{
					x = _typeClient.ResolveType(name, definition);
				}
				catch (System.IO.IOException e)
				{
					throw new TypeException(e.Message);
				}
				
				_resolveStack.RemoveAt( _resolveStack.Count-1 );
			}
			
			if (x == NOTYPE)
			{
				throw new TypeException("unknown type on server" + name);
			}
			
			Map(x, systemId);
			
			return x;
		}
		
		private void  resolveReverse(int id)
		{
			TypeTriple typeInfo;
			
			try
			{
				typeInfo = _typeClient.ResolveReverse(id);
			}
			catch (System.IO.IOException e)
			{
				throw new TypeException(e.Message);
			}
			
			int systemid = Library.GetId(typeInfo.Name);
			
			byte[] definition = TypeHelper.ToByteArray(this, Library.GetStructure(systemid));
			
			if (ByteArrayCompare.Compare(definition, typeInfo.Definition))
			{
				// The definition matches..  so it is ok.  Map it.
				Map(id, systemid);
				return ;
			}
			
			throw new TypeException("definition do not match");
		}
		
		
		public override int GetId(int systemId)
		{
			try
			{
				return base.GetId(systemId);
			}
			catch (TypeException)
			{
				return resolveType(systemId);
			}
		}
		
		public override int GetId(System.String name)
		{
			try
			{
				return base.GetId(name);
			}
			catch (TypeException)
			{
				try
				{
					int id = resolveType(Library.GetId(name));
					if (id == NOTYPE)
						throw new TypeException("not mapped");
					return id;
				}
				catch (TypeException ex2)
				{
					throw ex2;
				}
			}
		}
		
		public override int GetSystemId(int id)
		{
			try
			{
				// if successful it will just return.
				return base.GetSystemId(id);
			}
			catch (TypeException)
			{
				resolveReverse(id);
				return GetSystemId(id);
			}
		}
	}
}